import datetime
import os
from xml.dom import minidom
import cv2
import zipstream
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.shortcuts import render
from django.core.files.base import ContentFile
from pymysql import NULL
from rest_framework_jwt.settings import api_settings

from info_control import models
import rest_framework, rest_framework_jwt
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from django.http import JsonResponse, StreamingHttpResponse
from django.middleware.csrf import get_token
from django.http import HttpResponse
import json
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
# 上传图片的接口
# @require_http_methods(['POST'])
# @login_required
# def UploadImages(request):
#     new_image = models.ImageStorage(photo=, owner=request.user)


# 注册接口
@require_http_methods(['POST'])
def Register(request):
    try:
        obj = json.loads(request.body)
    except:
        return HttpResponse(json.dumps({'code': 99, 'message': '请求格式不是json!'}), status = 200)
    userId = obj.get("id", None)
    password = obj.get("password", None)
    emailAddress = obj.get("email", None)

    if userId is None or password is None or emailAddress is None:
        return HttpResponse(json.dumps({'code': 100, 'message': '请求参数不符'}), status=200)
    try:
        email_valid = User.objects.filter(email=emailAddress)
    except:
        return HttpResponse(json.dumps({'code': 110, 'message': '该电子邮箱已被注册'}), status=200)
    if email_valid.count() != 0:
        return HttpResponse(json.dumps({'code': 120, 'message': '该电子邮箱已被注册'}), status=200)
    try:
        user = User.objects.create_user(username = userId, password = password, email = emailAddress)
    except:
        return HttpResponse(json.dumps({'code': 130, 'message': '该用户名已被注册'}), status=200)
    return JsonResponse(
        {
            'code': 200,
        }
    )
# 不仅需要登陆，还需要授权给前端jwt token
@require_http_methods(['POST'])
def Login(request):
    try:
        obj = json.loads(request.body)
    except:
        return HttpResponse(json.dumps({'code': 400, 'message': '请求格式不是json!'}), status = 200)
    userId = obj.get("id", None)
    password = obj.get("password", None)
    if userId is None or password is None:
        return HttpResponse(json.dumps({'code': 400, 'message': '请求参数不符'}), status=200)
    is_login = authenticate(request, username=userId, password=password)
    if is_login is None:
        return HttpResponse(json.dumps({'code': 400, 'message': '用户名密码不匹配！'}), status=200)
    # 登陆成功
    jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
    jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
    payload = jwt_payload_handler(is_login)
    token = jwt_encode_handler(payload)
    print(token)
    return HttpResponse(json.dumps({'code': 200, 'token': token}), status=200)
# 获取CSRFtoken
def Get_csrf_token(request):
    csrf_token = get_token(request)  # 获取csrf_token的值
    return JsonResponse(data={'token': csrf_token})

# # 用于解码token
# # 如果传回false，那么返回的code要改成101----token失效，重新登陆
# import datetime
# def verifyToken(token):
#     jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
#     key = jwt_decode_handler(token)
#     now = datetime.datetime.now()
#     # 未过期
#     if (now < datetime.datetime.fromtimestamp(key['exp'])):
#         return True
#     else:
#         return False
#     pass

@require_http_methods(['POST'])
def UserName(request):
    obj = json.loads(request.body)
    token = obj.get("token")
    if token is not None:
        # 会自动检查token是否过期
        try:
            key = jwt_decode_handler(token)
        except:
            return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
        return HttpResponse(json.dumps({'code': 200, 'username': key['username']}), status=200)
    else:
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)


@require_http_methods(['POST'])
def UploadImage(request):
    # 获取图片
    token = request.POST.get("token")
    jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
    # 先从token中获取用户信息
    try:
        key = jwt_decode_handler(token)
    except:
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
    username = key['username']
    user = models.User.objects.filter(username=username)
    myImageCount = models.ImageStorage.objects.filter(owner = user[0]).count()
    print(myImageCount)
    files = request.FILES['file']
    files.name = username + "_" + str(datetime.datetime.now()) + files.name
    # 下面要存储图片
    mymodel = models.ImageStorage.objects.create(owner=user[0], photo=files)
    return HttpResponse(json.dumps({'code': 200, 'username': "success"}), status=200)


@require_http_methods(['POST'])
# 从requestbody中的token里面获取用户信息并返回与这个用户相关的图片供前端展示
def ShowUserImage(request):
    obj = json.loads(request.body)
    token = obj.get("token")
    print(token)
    try:
        key = jwt_decode_handler(token)
    except:
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
    username = key['username']
    user = models.User.objects.filter(username=username)
    Images = models.ImageStorage.objects.filter(owner=user[0])
    # 返回这些Images的路径
    ret = {}
    yes = {}
    ids = {}
    for i in range(len(Images)):
        path = Images[i].photo.path.split("media")[1]
        path = path.replace('\\', '/')
        path = "../../../media" + path
        ret.update({i: path})
        yes.update({i: Images[i].hasEstablished})
        ids.update({i:Images[i].id})
    # print(ret)
    # print(yes)
    return HttpResponse(json.dumps({'image': ret, 'yes': yes, 'id': ids, 'code': 200}), status=200)


@require_http_methods(['POST'])
# 删除用户指定的图片
def DeleteImages(request):
    # 需要检查token是否过期
    obj = json.loads(request.body)
    token = obj.get("token")
    try:
        key = jwt_decode_handler(token)
    except:
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
    ls = obj.get("deleteList")
    for ele in ls:
        queryset = models.ImageStorage.objects.filter(id = ele["ids"])
        delete_file(queryset[0])
        queryset.delete()
    return HttpResponse(json.dumps({'code': 200, 'message': 'Success'}), status=200)
    pass


# 删除本地图片
def delete_file(instance, **kwargs):
    instance.photo.delete(save=False)



# 创建任务
@require_http_methods(['POST'])
def CreateMission(request):
    # 从token中获取user
    obj = json.loads(request.body)
    token = obj.get("token")
    try:
        key = jwt_decode_handler(token)
    except:
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
    username = key['username']
    # 获取关联的user
    user = models.User.objects.filter(username=username)[0]
    # 从createList中获取需要创建的任务
    missionList = obj.get("createList")
    des = obj.get("des")
    date = datetime.datetime.today()
    # 创建Mission
    Mission = models.MissionStorage.objects.create(establisher=user, des=des, taker=None,date=date)
    # 创建tasks
    for mission in missionList:
        # 找到相关联的图片
        linkimage = models.ImageStorage.objects.filter(id=mission['ids'])[0]
        linkimage.hasEstablished = True
        linkimage.save()
        print(linkimage)
        models.TaskStorage.objects.create(belonging=Mission, photo=linkimage)
    return HttpResponse(json.dumps({'code': 200, 'message': 'success'}), status=200)
    pass


# 返回任务列表和每个任务的一张图片
@require_http_methods(['GET'])
def GetMissionsAndAPhoto(request):
    results = models.MissionStorage.objects.filter(taker=None)
    missions = []
    owners = []
    dess = []
    date = []
    count = []
    ids = []
    # 将没有人执行的任务取出放到任务大厅
    for result in results:
        ids.append(result.id)
        owners.append(result.establisher.username)
        dess.append(result.des)
        num = models.TaskStorage.objects.filter(belonging=result).count()
        oneTask = models.TaskStorage.objects.filter(belonging=result)[0]
        # 获取封面图片路径
        path = oneTask.photo.photo.path.split("media")[1].replace('\\', '/')
        missions.append("../../../media" + path)
        date.append(str(result.date))
        count.append(num)

    print(missions)
    print(dess)
    return HttpResponse(json.dumps({"code": 200, "ids":ids, "m": missions, "owners": owners, "dates": date, "des": dess, "number": count}), status=200)


# 用户领取任务接口
def ApplyAMission(request):
    obj = json.loads(request.body)
    token = obj.get("token")
    try:
        key = jwt_decode_handler(token)
    except:
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
    username = key['username']
    # 获取关联的user，检查这个user是否领取过别的任务
    user = models.User.objects.filter(username=username)[0]
    queryset = models.MissionStorage.objects.filter(taker=user)
    # 检查有没有任务还未完成
    for ele in range(len(queryset)):
        if not queryset[ele].finished:
            return HttpResponse(json.dumps({'code': 403, 'message': '这个用户有一项正在进行的任务!'}), status=200)
    # 让这个user领取任务
    missionId = obj.get("missionid")
    # 不允许自己领自己的任务
    missionChoice = models.MissionStorage.objects.filter(id=missionId)[0]
    if missionChoice.establisher == user:
        return HttpResponse(json.dumps({'code': 403, 'message': '不允许领取自己发布的任务!'}), status=200)
    # 领取任务，更改missionChoice的taker
    missionChoice.taker = user
    missionChoice.save()
    return HttpResponse(json.dumps({'code': 200, 'message': 'success'}), status=200)


# 查看个人发布的所有任务
@require_http_methods(['POST'])
def GetMyEstablishedMissions(request):
    obj = json.loads(request.body)
    token = obj.get("token")
    try:
        key = jwt_decode_handler(token)
    except:
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
    username = key['username']
    # 获取关联的user
    user = models.User.objects.filter(username=username)[0]
    results = models.MissionStorage.objects.filter(establisher=user)
    ids = []
    date = []
    dess = []
    finished = []
    hasTaken = []
    for result in results:
        ids.append(result.id)
        dess.append(result.des)
        date.append(str(result.date))
        finished.append(result.finished)
        if result.taker is None:
            hasTaken.append(False)
        else:
            hasTaken.append(True)
    return HttpResponse(json.dumps({"code": 200, "ids":ids, "dates": date, "des": dess, "hastaken": hasTaken, "finished": finished}), status=200)


# 提取文件中上传的视频，并按规则截取帧，存到该用户的存储中
# 使用cv2处理视频
def VideoUpload(request):
    token = request.POST.get("token")
    jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
    # 先从token中获取用户信息
    try:
        key = jwt_decode_handler(token)
    except:
        print("wrong")
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
    username = key['username']
    user = models.User.objects.filter(username=username)[0]
    # 获取视频文件
    videofile = request.FILES.get("file")
    # 先要存储到本地
    filename = '.\\static\\videos\\' + 'video_' + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + "." + videofile.name.split('.')[-1]
    with open(filename, 'wb+') as f:
        f.write(videofile.read())
    f.close()
    cap = cv2.VideoCapture(filename)
    i = 1
    if cap.isOpened():
        rval, frame = cap.read()
    else:
        rval = False

    while rval:
        dir_path = os.path.dirname(os.path.abspath(__file__))
        rval, frame = cap.read()
        # 每48帧截取一次
        if (i % 48 == 1):
            # 截出的帧的名字
            path="./media/images"
            if not os.path.exists(path):  # 如果不存在就创建文件夹
                os.mkdir(path)
            shot_name = username + str(datetime.datetime.now()).replace(":", "").replace(".", "x").replace(" ", "")+".jpg"
            f = open(path + "/" + shot_name, 'w')
            f.close()
            # 存储在本地, 建立数据库中图片与实际保存的关联
            print(dir_path)
            dir_path = dir_path.split('\\')
            print(dir_path)
            mypath = dir_path[0]
            for i in range(len(dir_path) - 2):
                mypath = mypath + "/" +  dir_path[i + 1]
            print(mypath)
            mypath = mypath + "/" + "media" +"/" +  "images" + "/"
            cv2.imencode('.jpg', frame)[1].tofile(mypath+shot_name)
            models.ImageStorage.objects.create(owner=user, photo="images/" + shot_name)
            cv2.waitKey(1)
        i = i + 1
    # 释放资源
    cap.release()
    # 删除在本地缓存的视频
    os.remove(filename)
    return HttpResponse(json.dumps({'code': 200, 'message': 'success'}), status=200)


# 获取所有自己领取的任务
@require_http_methods(['POST'])
def GetMyTakenMission(request):
    ids = []
    date = []
    dess = []
    finished = []
    obj = json.loads(request.body)
    token = obj.get("token")
    try:
        key = jwt_decode_handler(token)
    except:
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
    username = key['username']
    user = models.User.objects.filter(username=username)[0]
    queryset = models.MissionStorage.objects.filter(taker=user)
    for result in queryset:
        ids.append(result.id)
        dess.append(result.des)
        date.append(str(result.date))
        finished.append(result.finished)
    return HttpResponse(
        json.dumps({"code": 200, "ids": ids, "des": dess, "finished": finished}),status=200)

# 删除任务：要求是这个任务没有被人领取或被领取但已经完成
@require_http_methods(['POST'])
def DeleteMission(request):
    obj = json.loads(request.body)
    ls = obj.get("deletelist")
    token = obj.get("token")
    try:
        key = jwt_decode_handler(token)
    except:
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
    username = key['username']
    user = models.User.objects.filter(username=username)[0]
    for item in ls:
        missions = models.MissionStorage.objects.filter(id=item["ids"])
        for query in missions:
            if query.establisher == user:
                photos = models.TaskStorage.objects.filter(belonging=query)
                # 更新任务涉及图片的发布状态
                for photo in photos:
                    photo.photo.hasEstablished = False
                    photo.photo.save()
                query.delete()
    return HttpResponse(json.dumps({'code': 200, 'message': 'success'}), status=200)

# 获取某项任务未完成的图片列表
@require_http_methods(['POST'])
def GetTaskImages(request):
    # 首先通过user找到他还未完成的任务
    obj = json.loads(request.body)
    token = obj.get("token")
    try:
        key = jwt_decode_handler(token)
    except:
        return HttpResponse(json.dumps({'code': 101, 'message': 'login again'}), status=200)
    username = key['username']
    user = models.User.objects.filter(username=username)[0]
    try:
        unfinishedMission = models.MissionStorage.objects.get(taker=user,finished=False)
    except Exception:
        return HttpResponse(json.dumps({'code': 100, 'message': '您当前没有未完成的任务!'}), status=200)
    # 所有未完成的tasks
    tasks = models.TaskStorage.objects.filter(belonging=unfinishedMission, finished=False)
    # 如果没有未完成的task了：
    if len(tasks) == 0:
        unfinishedMission.finished=True
        unfinishedMission.save()
        return HttpResponse(json.dumps({'code': 100, 'message': '您已经完成当前任务中的所有图片标注工作!'}), status=200)
    ret = []
    ids = []
    for task in tasks:
        image = task.photo.photo
        imageurl = image.path.split("media")[1].replace('\\', '/')
        imageurl = "../../../media" + imageurl
        ids.append(task.photo.id)
        ret.append(imageurl)
    return HttpResponse(json.dumps({'code': '200', 'urls': ret, 'ids': ids}), status=200)




# 生成PV格式的数据集并保存标注结果
def GeneratePascalVoc(filename, filepath, relatetask, data):
    mouse1 = data.get("mouse1")
    mouse2 = data.get("mouse2")
    labels = data.get("labels")
    height = str(data.get("height"))
    width = str(data.get("width"))
    mission = relatetask.belonging
    foldername = mission.establisher.username + str(mission.id)
    # 创建dom树对象
    dom = minidom.getDOMImplementation().createDocument(None, 'annotation', None)
    # 创建根结点
    root_node = dom.documentElement
    # folder节点
    folder_node = dom.createElement('folder')
    folder_text = dom.createTextNode(foldername)
    folder_node.appendChild(folder_text)
    # 文件名节点
    filename_node = dom.createElement('filename')
    filename_text = dom.createTextNode(filename)
    filename_node.appendChild(filename_text)

    root_node.appendChild(folder_node)
    root_node.appendChild(filename_node)

    size_node = dom.createElement('size')
    # 宽度节点
    width_node = dom.createElement('width')
    width_text = dom.createTextNode(width)
    width_node.appendChild(width_text)
    size_node.appendChild(width_node)
    # 高度节点
    height_node = dom.createElement('height')
    height_text = dom.createTextNode(height)
    height_node.appendChild(height_text)
    size_node.appendChild(height_node)
    # 深度节点
    depth_node = dom.createElement('depth')
    depth_text = dom.createTextNode('3')
    depth_node.appendChild(depth_text)
    size_node.appendChild(depth_node)
    # root node加上size node
    root_node.appendChild(size_node)
    print(labels)
    # 遍历，将所有object标签提取出来
    for i in range(len(labels)):
        object_node = dom.createElement('object')
        # 计算xmin\xmax\ymin\ymax
        if mouse1[i][0] < mouse2[i][0]:
            xmin = str(mouse1[i][0])
            xmax = str(mouse2[i][0])
        else:
            xmin = str(mouse2[i][0])
            xmax = str(mouse1[i][0])
        if mouse1[i][1] < mouse2[i][1]:
            ymin = str(mouse1[i][1])
            ymax = str(mouse2[i][1])
        else:
            ymin = str(mouse2[i][1])
            ymax = str(mouse1[i][1])
        name_node = dom.createElement('name')
        name_text = dom.createTextNode(labels[i])
        name_node.appendChild(name_text)
        object_node.appendChild(name_node)

        bndbox_node = dom.createElement('bndbox')
        xmin_node = dom.createElement('xmin')
        xmin_text = dom.createTextNode(xmin)
        xmin_node.appendChild(xmin_text)

        xmax_node = dom.createElement('xmax')
        xmax_text = dom.createTextNode(xmax)
        xmax_node.appendChild(xmax_text)

        ymin_node = dom.createElement('ymin')
        ymin_text = dom.createTextNode(ymin)
        ymin_node.appendChild(ymin_text)

        ymax_node = dom.createElement('ymax')
        ymax_text = dom.createTextNode(ymax)
        ymax_node.appendChild(ymax_text)
        # bndbox节点加上上面的东西，还要加上truncated和difficult
        bndbox_node.appendChild(xmin_node)
        bndbox_node.appendChild(xmax_node)
        bndbox_node.appendChild(ymin_node)
        bndbox_node.appendChild(ymax_node)
        object_node.appendChild(bndbox_node)

        truncated_node = dom.createElement('truncated')
        difficult_node = dom.createElement('difficult')
        truncated_text = dom.createTextNode('0')
        truncated_node.appendChild(truncated_text)
        difficult_text = dom.createTextNode('0')
        difficult_node.appendChild(difficult_text)
        object_node.appendChild(truncated_node)
        object_node.appendChild(difficult_node)
        root_node.appendChild(object_node)
    seg_node = dom.createElement('segmented')
    seg_text = dom.createTextNode('0')
    seg_node.appendChild(seg_text)
    root_node.appendChild(seg_node)
    # 创建文件并存储
    if not (os.path.exists('media/' + filepath + foldername)):
        os.mkdir('media/' + filepath + foldername)
    with open('media/' + filepath + foldername + '/' + filename, "wb") as f:
        f.write(root_node.toprettyxml(indent="\t", newl="\n", encoding="utf-8"))
        f.close()
        relatetask.finishedfile = filepath + foldername + '/' + filename
        relatetask.save()
    return True


from cocoxml import parseXmlFiles
# 生成COCO格式数据集并保存
def GenerateCOCO(filename, filepath, relatetask):
    filep = filepath + 'COCO'
    filename = filename + '.json'
    if os.path.exists("." + filep) is False:
        os.mkdir("." + filep)
    fullfileurl = "." + filep
    parseXmlFiles("." + filepath, fullfileurl+"/"+filename)
    return fullfileurl


# 保存一个task的标注结果
@require_http_methods(['POST'])
def saveMarking(request):
    obj = json.loads(request.body)
    imageid = obj.get("id")
    mouse1 = obj.get("mouse1")
    mouse2 = obj.get("mouse2")
    print(mouse2)
    print(mouse1)
    if imageid is None or mouse1 is None or mouse2 is None:
        return HttpResponse(json.dumps({'code': 101, 'message': 'Wrong request!'}), status=200)
    # 建一个PASCAL VOC数据集格式的文件存储这些信息
    accociatedPhoto = models.ImageStorage.objects.get(id=imageid)
    photoName = str(accociatedPhoto.id)
    accociatedTask = models.TaskStorage.objects.get(photo=accociatedPhoto)
    # 生成PASCAL VOC文件并保存
    if GeneratePascalVoc(photoName + "PV.xml", "static/dataset/", accociatedTask, obj):
        # 更改task状态
        accociatedTask.finished = True
        accociatedTask.save()
        pass
    else:
        return HttpResponse(json.dumps({'code': 100, 'message': '文件保存失败!'}), status=200)
    return HttpResponse(json.dumps({'code': 200, 'message': 'Done!'}), status=200)


# 下载文件
def downloadFile(request):
    obj = json.loads(request.body)
    way = obj.get("way")
    print(way)
    # 需要下载的task图片文件
    missionId = obj.get("ids")
    # 找到对应的mission，查看完成情况
    relatedMission = models.MissionStorage.objects.filter(id=missionId)[0]
    relatedTask = models.TaskStorage.objects.filter(belonging=relatedMission)[0]
    taskPath = relatedTask.finishedfile.url
    folder, filename = os.path.split(taskPath)
    if relatedMission.finished is False:
        print("falut1")
        return HttpResponse(json.dumps({'code': 99, 'message': '该任务还没有标注完毕'}), status=200)
    if way == '1':
        # 下载Pascalvoc格式
        # 压缩包
        folder = "./"+folder
        zipFile = zipstream.ZipFile(mode='w', compression=zipstream.ZIP_DEFLATED)
        for file in os.listdir(folder):
            full_path = os.path.join(folder, file)
            if os.path.isfile(full_path):
                zipFile.write(full_path, arcname=os.path.join("PASCAL VOC", os.path.basename(full_path)))

        response = StreamingHttpResponse(zipFile, content_type='application/zip')
        response['Content-Disposition'] = 'attachment;filename="{0}"'.format("download.zip")
        return response
        zipFile.close()
    # coco格式
    else:
        # 先要转格式，再下载为coco格式
        folder = GenerateCOCO(filename.split('.')[0], folder, relatedTask)
        print(folder)
        zipFile = zipstream.ZipFile(mode='w', compression=zipstream.ZIP_DEFLATED)
        for file in os.listdir(folder):
            full_path = os.path.join(folder, file)
            if os.path.isfile(full_path):
                zipFile.write(full_path, arcname=os.path.join("COCO", os.path.basename(full_path)))
            response = StreamingHttpResponse(zipFile, content_type='application/zip')
            response['Content-Disposition'] = 'attachment;filename="{0}"'.format("download.zip")
            return response
            zipFile.close()

